package com.swingson.properties.specificclass_impl;

import com.swingson.Swingson;
import com.swingson.SwingsonGui;
import com.swingson.jsonparser.JsonElement;
import com.swingson.other.ConditionAndAction;
import com.swingson.other.MethodExecutor;
import com.swingson.properties.AbstractAssignMethodPropertyProcessor;
import java.awt.Point;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;

/**
 * @author sad
 */
public class MouseActionPropertyProcessor extends AbstractAssignMethodPropertyProcessor {

    public static final String mouseMove = "mouseMoved";
    public static final String mouseDrag = "mouseDragged";
    public static final String mouseClicked = "mouseClicked";
    public static final String mousePressed = "mousePressed";
    public static final String mouseReleased = "mouseReleased";
    public static final String mouseEntered = "mouseEntered";
    public static final String mouseExited = "mouseExited";
    public static final String mouseWheel = "mouseWheel";

    static List<Class[]> argumentsVariants = new ArrayList<Class[]>() {
        {
            add(new Class[]{Point.class});
            add(new Class[]{Point.class, Button.class});
            add(new Class[]{Point.class, Button.class, int.class});//position, button, modifier
            add(new Class[]{Point.class, Button.class, int.class, int.class});//position, button, modifier, click count
            add(new Class[]{});//no parameters
            add(new Class[]{MouseEvent.class});//no parameters
        }
    };

    @Override
    protected String getName() {
        return "Mouse Event Listener";
    }

    @Override
    protected List<Class[]> methodArgumentsVariants() {
        return argumentsVariants;
    }

    @Override
    protected void attachMethod(JsonElement jsonElement, final JComponent component, final SwingsonGui gui, String propertyName, final MethodExecutor mExecutor, final int methodArgumentVariant, final ConditionAndAction conditionAndAction) {
        EventExecutor executor = new EventExecutor<MouseEvent>() {
            @Override
            public void execute(MouseEvent event) {
                if (!Swingson.isEnabledUserInput()) {
                    return;
                }
                try {
                    switch (methodArgumentVariant) {
                        case 0:
                            mExecutor.invoke(event.getPoint());
                            break;
                        case 1:
                            mExecutor.invoke(event.getPoint(), new Button(event.getButton()));
                            break;
                        case 2:
                            mExecutor.invoke(event.getPoint(), new Button(event.getButton()), event.getModifiersEx());
                            break;
                        case 3:
                            mExecutor.invoke(event.getPoint(), new Button(event.getButton()), event.getModifiersEx(), event.getClickCount());
                            break;
                        case 4:
                            mExecutor.invoke();
                            break;
                        case 5:
                            mExecutor.invoke(event);
                            break;
                    }

                    if (conditionAndAction != null) {
                        if (conditionAndAction.shouldProcess("mouse_x")) {
                            gui.setVariableSilently("mouse_x", event.getPoint().x, true);
                        }
                        if (conditionAndAction.shouldProcess("mouse_y")) {
                            gui.setVariableSilently("mouse_y", event.getPoint().y, true);
                        }
                        if (conditionAndAction.shouldProcess("mouse_button")) {
                            gui.setVariableSilently("mouse_button", event.getButton(), true);
                        }

                        if (conditionAndAction.shouldProcess("mouse_click_count")) {
                            gui.setVariableSilently("mouse_click_count", event.getClickCount(), true);
                        }
                    }
                    runAction(conditionAndAction, gui, component);
                } catch (Throwable ex) {
                    onError(gui, ex);
                }
            }
        };

        if (propertyName.equals(mouseWheel)) {
            for (MouseWheelListener listener : component.getMouseWheelListeners()) {
                if (listener instanceof MouseWheelListenerWrapper) {
                    ((MouseWheelListenerWrapper) listener).setOnWheelMoved(executor);
                    return;
                }
            }

            MouseWheelListenerWrapper listener = new MouseWheelListenerWrapper();
            listener.setOnWheelMoved(executor);
            component.addMouseWheelListener(listener);
        } else if (propertyName.equals(mouseMove) || propertyName.equals(mouseDrag)) {
            for (MouseMotionListener listener : component.getMouseMotionListeners()) {
                if (listener instanceof MouseMotionListenerWrapper) {
                    if (propertyName.equals(mouseMove)) {
                        ((MouseMotionListenerWrapper) listener).setOnMoved(executor);
                    } else {
                        ((MouseMotionListenerWrapper) listener).setOnDragged(executor);
                    }
                    return;
                }
            }

            MouseMotionListenerWrapper listener = new MouseMotionListenerWrapper();
            if (propertyName.equals(mouseMove)) {
                listener.setOnMoved(executor);
            } else {
                listener.setOnDragged(executor);
            }

            component.addMouseMotionListener(listener);
        } else if (propertyName.equals(mouseClicked) || propertyName.equals(mouseEntered) || propertyName.equals(mouseExited) || propertyName.equals(mousePressed) || propertyName.equals(mouseReleased)) {
            for (MouseListener listener : component.getMouseListeners()) {
                if (listener instanceof MouseListenerWrapper) {
                    switch (propertyName) {
                        case mouseClicked:
                            ((MouseListenerWrapper) listener).setClicked(executor);
                            break;
                        case mouseEntered:
                            ((MouseListenerWrapper) listener).setEntered(executor);
                            break;
                        case mouseExited:
                            ((MouseListenerWrapper) listener).setExited(executor);
                            break;
                        case mousePressed:
                            ((MouseListenerWrapper) listener).setPressed(executor);
                            break;
                        case mouseReleased:
                            ((MouseListenerWrapper) listener).setReleased(executor);
                            break;
                    }
                    return;
                }

            }

            MouseListenerWrapper listener = new MouseListenerWrapper();
            switch (propertyName) {
                case mouseClicked:
                    listener.setClicked(executor);
                    break;
                case mouseEntered:
                    listener.setEntered(executor);
                    break;
                case mouseExited:
                    listener.setExited(executor);
                    break;
                case mousePressed:
                    listener.setPressed(executor);
                    break;
                case mouseReleased:
                    listener.setReleased(executor);
                    break;
            }

            component.addMouseListener(listener);
        }
    }

    private static class MouseMotionListenerWrapper implements MouseMotionListener {

        private EventExecutor onDragged;
        private EventExecutor onMoved;

        public void setOnDragged(EventExecutor onDragged) {
            this.onDragged = onDragged;
        }

        public void setOnMoved(EventExecutor onMoved) {
            this.onMoved = onMoved;
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (onDragged != null) {
                onDragged.execute(e);
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            if (onMoved != null) {
                onMoved.execute(e);
            }
        }
    }

    private static class MouseWheelListenerWrapper implements MouseWheelListener {

        private EventExecutor onWheelMoved;

        public void setOnWheelMoved(EventExecutor onWheelMoved) {
            this.onWheelMoved = onWheelMoved;
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            if (onWheelMoved != null) {
                onWheelMoved.execute(e);
            }
        }
    }

    private static class MouseListenerWrapper implements MouseListener {

        private EventExecutor clicked;
        private EventExecutor pressed;
        private EventExecutor released;
        private EventExecutor entered;
        private EventExecutor exited;

        public void setClicked(EventExecutor clicked) {
            this.clicked = clicked;
        }

        public void setPressed(EventExecutor pressed) {
            this.pressed = pressed;
        }

        public void setReleased(EventExecutor released) {
            this.released = released;
        }

        public void setEntered(EventExecutor entered) {
            this.entered = entered;
        }

        public void setExited(EventExecutor exited) {
            this.exited = exited;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            if (clicked != null) {
                clicked.execute(e);
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
            if (pressed != null) {
                pressed.execute(e);
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (released != null) {
                released.execute(e);
            }
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            if (entered != null) {
                entered.execute(e);
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            if (exited != null) {
                exited.execute(e);
            }
        }
    }

    public static class Button {

        public Button(int b) {
            this.button = b;
        }
        private int button;

        public int getButton() {
            return button;
        }

        public void setButton(int button) {
            this.button = button;
        }

        @Override
        public String toString() {
            return "Button " + button;
        }

    }
}
